(function() {

    var alt, alternatives, compilerSettings, grammar, name, o, operators, token, tokens, unwrap;

    unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/;

    o = function(patternString, action, options) {

        var match;

        patternString = patternString.replace(/\s{2,}/g, ' ');

        if (!action) {
            return [patternString, '$$ = $1;', options];
        }

        action = (match = unwrap.exec(action)) ? match[1] : "(" + action + "())";
        action = action.replace(/\bnew /g, '$&yy.');

        return [patternString, "$$ = " + action + ";", options];

    };

    grammar = {
        Root: [o("AllQuery EOF")],
        AllQuery: [o("Query"), o("ExplainQuery")],
        ExplainQuery: [
            o("EXPLAIN QUERY PLAN SelectExpression", function() {
                return new QueryPlan($4);
            })
        ],
        Query: [o("SelectExpression"), o("InsertQuery"), o("DeleteQuery"), o("UpdateQuery"), o("CreateTableQuery"), o("DropTableQuery"), o("AlterTableQuery")],
        SelectExpression: [
            o("SelectQuery"), o("SelectQuery Unions", function() {
                $1.unions = $2;
                return $1;
            })
        ],
        SelectQuery: [o("SelectWithLimitQuery"), o("BasicSelectQuery")],
        BasicSelectQuery: [
            o('Select'), o('Select OrderClause', function() {
                $1.order = $2;
                return $1;
            }), o('Select GroupClause', function() {
                $1.group = $2;
                return $1;
            }), o('Select GroupClause OrderClause', function() {
                $1.group = $2;
                $1.order = $3;
                return $1;
            })
        ],
        SelectWithLimitQuery: [
            o('SelectQuery LimitClause', function() {
                $1.limit = $2;
                return $1;
            })
        ],
        Select: [
            o('SelectClause'), o('SelectClause WhereClause', function() {
                $1.where = $2;
                return $1;
            })
        ],
        SelectClause: [
            o('SELECT Fields FROM Table', function() {
                return new Select($2, $4, false);
            }), o('SELECT DISTINCT Fields FROM Table', function() {
                return new Select($3, $5, true);
            }), o('SELECT Fields FROM Table Joins', function() {
                return new Select($2, $4, false, $5);
            }), o('SELECT DISTINCT Fields FROM Table Joins', function() {
                return new Select($3, $5, true, $6);
            })
        ],
        InsertQuery: [
            o("INSERT INTO Literal VALUES LEFT_PAREN ArgumentList RIGHT_PAREN", function() {
                return new Insert($3, $6);
            }), o("INSERT INTO Literal SelectQuery", function() {
                return new Insert($3, $4);
            }), o("INSERT INTO Literal LEFT_PAREN Fields RIGHT_PAREN VALUES LEFT_PAREN ArgumentList RIGHT_PAREN", function() {
                return new Insert($3, $9, $5);
            }), o("INSERT INTO Literal LEFT_PAREN Fields RIGHT_PAREN SelectQuery", function() {
                return new Insert($3, $7, $5);
            })
        ],
        DeleteQuery: [
            o("DELETE FROM Literal", function() {
                return new Delete($3);
            }), o("DELETE FROM Literal WhereClause", function() {
                return new Delete($3, $4);
            })
        ],
        UpdateQuery: [
            o("UPDATE Literal SET AssignmentList", function() {
                return new Update($2, $4);
            }), o("UPDATE Literal SET AssignmentList WhereClause", function() {
                return new Update($2, $4, $5);
            })
        ],
        CreateTableQuery: [
            o('CREATE TABLE Literal LEFT_PAREN FieldDefList RIGHT_PAREN', function() {
                return new CreateTable($3, $5);
            }), o('CREATE TABLE DIRECTIVES Literal LEFT_PAREN FieldDefList RIGHT_PAREN', function() {
                return new CreateTable($4, $6, true);
            })
        ],
        DropTableQuery: [
            o('DROP TABLE Literal', function() {
                return new DropTable($3, false);
            }), o('DROP TABLE DIRECTIVES Literal', function() {
                return new DropTable($4, true);
            })
        ],
        AlterTableQuery: [
            o('ALTER TABLE Literal RENAME TO Literal', function() {
                return new AlterTable($3, $6);
            })
        ],
        Table: [
            o('Literal', function() {
                return new Table($1);
            }), o('Literal AS Literal', function() {
                return new Table($1, $3);
            }), o('Literal Literal', function() {
                return new Table($1, $2);
            }), o('LEFT_PAREN SelectExpression RIGHT_PAREN', function() {
                return new SubSelect($2);
            }), o('LEFT_PAREN SelectExpression RIGHT_PAREN Literal', function() {
                return new SubSelect($2, $4);
            }), o('LEFT_PAREN SelectExpression RIGHT_PAREN AS Literal', function() {
                return new SubSelect($2, $5);
            }), o('Literal WINDOW WINDOW_FUNCTION LEFT_PAREN Number RIGHT_PAREN', function() {
                return new Table($1, $2, $3, $5);
            })
        ],
        SubQuery: [
            o('LEFT_PAREN SelectExpression RIGHT_PAREN', function() {
                return new SubQuery($2);
            })
        ],
        Unions: [
            o('Union', function() {
                return [$1];
            }), o('Unions Union', function() {
                return $1.concat($2);
            })
        ],
        Union: [
            o('UNION SelectQuery', function() {
                return new Union($2);
            }), o('UNION ALL SelectQuery', function() {
                return new Union($3, true);
            })
        ],
        Joins: [
            o('Join', function() {
                return [$1];
            }), o('Joins Join', function() {
                return $1.concat($2);
            })
        ],
        Join: [
            o('JOIN Table ON Expression', function() {
                return new Join($2, $4);
            }), o('LEFT JOIN Table ON Expression', function() {
                return new Join($3, $5, 'LEFT', 'OUTER');
            }), o('RIGHT JOIN Table ON Expression', function() {
                return new Join($3, $5, 'RIGHT', 'OUTER');
            }), o('LEFT INNER JOIN Table ON Expression', function() {
                return new Join($4, $6, 'LEFT', 'INNER');
            }), o('INNER JOIN Table ON Expression', function() {
                return new Join($3, $5, 'LEFT', 'INNER');
            }), o('RIGHT INNER JOIN Table ON Expression', function() {
                return new Join($4, $6, 'RIGHT', 'INNER');
            }), o('LEFT OUTER JOIN Table ON Expression', function() {
                return new Join($4, $6, 'LEFT', 'OUTER');
            }), o('RIGHT OUTER JOIN Table ON Expression', function() {
                return new Join($4, $6, 'RIGHT', 'OUTER');
            }), o('SEPARATOR Table', function() {
                return new Join($2, null, 'LEFT', 'INNER');
            })
        ],
        WhereClause: [
            o('WHERE Expression', function() {
                return new Where($2);
            })
        ],
        LimitClause: [
            o('LIMIT Expression', function() {
                return new Limit(null, $2);
            }), o('LIMIT Expression SEPARATOR Expression', function() {
                return new Limit($2, $4);
            })
        ],
        OrderClause: [
            o('ORDER BY OrderArgs', function() {
                return new Order($3);
            })
        ],
        OrderArgs: [
            o('OrderArg', function() {
                return [$1];
            }), o('OrderArgs SEPARATOR OrderArg', function() {
                return $1.concat($3);
            })
        ],
        OrderArg: [
            o('Expression', function() {
                return new OrderArgument($1, 'ASC');
            }), o('Expression DIRECTION', function() {
                return new OrderArgument($1, $2);
            }), o('Expression COLLATE NOCASE', function() {
                return new OrderArgument($1, 'ASC', 'NOCASE');
            }), o('Expression COLLATE NOCASE DIRECTION', function() {
                return new OrderArgument($1, $4, 'NOCASE');
            })
        ],
        GroupClause: [
            o('GroupBasicClause'), o('GroupBasicClause HavingClause', function() {
                $1.having = $2;
                return $1;
            })
        ],
        GroupBasicClause: [
            o('GROUP BY ArgumentList', function() {
                return new Group($3);
            })
        ],
        HavingClause: [
            o('HAVING Expression', function() {
                return new Having($2);
            })
        ],
        CaseConditions: [
            o('CaseCondition', function() {
                return [$1];
            }), o('CaseConditions CaseCondition', function() {
                return $1.concat($2);
            })
        ],
        CaseCondition: [
            o('WHEN Expression THEN Expression', function() {
                return new FunctionValue('CASE_CONDITION', [$2, $4]);
            })
        ],
        Expression: [
            o('SubQuery'), o('LEFT_PAREN Expression RIGHT_PAREN', function() {
                return $2;
            }), o('Expression STRING_OP Expression', function() {
                return new Op($2, $1, $3);
            }), o('Expression MATH Expression', function() {
                return new Op($2, $1, $3);
            }), o('Expression MATH_MULTI Expression', function() {
                return new Op($2, $1, $3);
            }), o('Expression OPERATOR Expression', function() {
                return new Op($2, $1, $3);
            }), o('Expression CONDITIONAL Expression', function() {
                return new Op($2, $1, $3);
            }), o('Literal NOT IN LEFT_PAREN ArgumentList RIGHT_PAREN', function() {
                return new Op('NOT_IN', $1, $5);
            }), o('Literal NOT IN SubQuery', function() {
                return new Op('NOT_IN', $1, $4);
            }), o('Literal IN LEFT_PAREN ArgumentList RIGHT_PAREN', function() {
                return new Op('IN', $1, $4);
            }), o('Literal IN SubQuery', function() {
                return new Op('IN', $1, $3);
            }), o('CASE CaseConditions END', function() {
                return new FunctionValue('CASE', [$2, void 0, void 0]);
            }), o('CASE Expression CaseConditions END', function() {
                return new FunctionValue('CASE', [$3, $2, void 0]);
            }), o('CASE CaseConditions ELSE Expression END', function() {
                return new FunctionValue('CASE', [$2, void 0, $4]);
            }), o('CASE Expression CaseConditions ELSE Expression END', function() {
                return new FunctionValue('CASE', [$3, $2, $5]);
            }), o('Value')
        ],
        Value: [o('Literal'), o('Number'), o('Boolean'), o('String'), o('Function'), o('UserFunction'), o('PlaceHolder')],
        Literal: [
            o('LITERAL', function() {
                return new LiteralValue($1);
            }), o('LEFT_BRACKET LITERAL RIGHT_BRACKET', function() {
                return new LiteralValue($2);
            }), o('Literal DOT LITERAL', function() {
                return new LiteralValue($1, $3);
            }), o('FUNCTION', function() {
                return new LiteralValue($1);
            })
        ],
        Number: [
            o('NUMBER', function() {
                return new NumberValue($1);
            }), o('MATH NUMBER', function() {
                return new NumberValue($2, $1);
            })
        ],
        Boolean: [
            o('BOOLEAN', function() {
                return new BooleanValue($1);
            })
        ],
        String: [
            o('STRING', function() {
                return new StringValue($1, "'");
            }), o('DBLSTRING', function() {
                return new StringValue($1, '"');
            })
        ],
        Function: [
            o("FUNCTION LEFT_PAREN Expression AS Literal RIGHT_PAREN", function() {
                return new FunctionCastValue($1, $3, $5, false);
            }), o("FUNCTION LEFT_PAREN STAR RIGHT_PAREN", function() {
                return new FunctionValue($1, [$3], false, false);
            }), o("FUNCTION LEFT_PAREN ArgumentList RIGHT_PAREN", function() {
                return new FunctionValue($1, $3, false, false);
            }), o("FUNCTION LEFT_PAREN DISTINCT Expression RIGHT_PAREN", function() {
                return new FunctionValue($1, [$4], false, true);
            })
        ],
        UserFunction: [
            o("LITERAL LEFT_PAREN ArgumentList RIGHT_PAREN", function() {
                return new FunctionValue($1, $3, true);
            })
        ],
        PlaceHolder: [
            o('PARAM_PLACEHOLDER', function() {
                var args, escFn, parser, placeholders, value;
                parser = yy.parser;
                args = parser.args;
                placeholders = parser.placeholders;
                escFn = parser.escFn;
                if (args.length !== placeholders.length) {
                    parser.throwUnMatchedParamsExc();
                }
                value = args.shift();
                placeholders.shift();
                value = escFn(value);
                if (value === null || value === undefined) {
                    return value;
                }
                switch (typeof value) {
                    case "string":
                        return new StringValue(value, "'");
                    case "number":
                        return new NumberValue(Math.abs(value), (value > -1 ? "+" : "-"));
                    default:
                        return String(value);
                }
            })
        ],
        ArgumentList: [
            o('Expression', function() {
                return [$1];
            }), o('ArgumentList SEPARATOR Expression', function() {
                return $1.concat($3);
            })
        ],
        AssignmentList: [
            o('Expression', function() {
                return [$1];
            }), o('AssignmentList SEPARATOR Expression', function() {
                return $1.concat($3);
            })
        ],
        Fields: [
            o('Field', function() {
                return [$1];
            }), o('Fields SEPARATOR Field', function() {
                return $1.concat($3);
            })
        ],
        Field: [
            o('STAR', function() {
                return new Star('');
            }), o('Literal DOT STAR', function() {
                return new Star($1);
            }), o('Expression', function() {
                return new Field($1, $1);
            }), o('Expression AS Literal', function() {
                return new Field($1, $3);
            }), o('Expression AS String', function() {
                return new Field($1, $3);
            })
        ],
        FieldDefList: [
            o('FieldDef', function() {
                return [$1];
            }), o('FieldDefList SEPARATOR FieldDef', function() {
                return $1.concat($3);
            })
        ],
        FieldDef: [
            o('Literal', function() {
                return new FieldDef($1, 'text', true, false, false);
            }), o('Literal Literal', function() {
                return new FieldDef($1, $2, true, false, false);
            }), o('Literal Literal FIELD_DEF_NOT_NULL', function() {
                return new FieldDef($1, $2, false, false, false);
            }), o('Literal Literal FIELD_DEF_NOT_NULL PRIMARY KEY', function() {
                return new FieldDef($1, $2, false, true, false);
            }), o('Literal Literal FIELD_DEF_NOT_NULL PRIMARY KEY AUTOINCREMENT', function() {
                return new FieldDef($1, $2, false, true, true);
            }), o('Literal Literal PRIMARY KEY', function() {
                return new FieldDef($1, $2, true, true, false);
            }), o('Literal Literal PRIMARY KEY AUTOINCREMENT', function() {
                return new FieldDef($1, $2, true, true, true);
            })
        ]
    };

    tokens = [];

    operators = [
        ['left', 'Op'],
        ['left', 'MATH_MULTI'],
        ['left', 'MATH'],
        ['left', 'STRING_OP'],
        ['left', 'OPERATOR'],
        ['left', 'CONDITIONAL']
    ];

    for (name in grammar) {
        alternatives = grammar[name];
        grammar[name] = (function() {
            var _i, _j, _len, _len1, _ref, _results;
            _results = [];
            for (_i = 0, _len = alternatives.length; _i < _len; _i++) {
                alt = alternatives[_i];
                _ref = alt[0].split(' ');
                for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
                    token = _ref[_j];
                    if (!grammar[token]) {
                        tokens.push(token);
                    }
                }
                if (name === 'Root') {
                    alt[1] = "return " + alt[1];
                }
                _results.push(alt);
            }
            return _results;
        })();
    }


    exports.grammar = {
        tokens: tokens.join(' '),
        bnf: grammar,
        operators: operators.reverse(),
        startSymbol: 'Root'
    };

}());